/** * Copyright 2016 Nikita Koksharov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.redisson.spring.support; import org.redisson.spring.misc.BeanMethodInvoker; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.MethodInvokingFactoryBean; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.core.Conventions; import org.springframework.util.StringUtils; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * * @author Rui Gu (https://github.com/jackygurui) */ public class RedissonNamespaceParserSupport { public final static String REDISSON_NAMESPACE = "http://redisson.org/schema/redisson"; static final String API_CLASS_PATH_PREFIX = "org.redisson.api.R"; static final String IMPL_CLASS_PATH_PREFIX = "org.redisson.Redisson"; static final String ID_ATTRIBUTE = "id"; static final String NAME_ATTRIBUTE = "name"; static final String REDISSON_REF_ATTRIBUTE = "redisson-ref"; static final String READ_WRITE_LOCK_REF_ATTRIBUTE = "read-write-lock-ref"; static final String EXECUTOR_REF_ATTRIBUTE = "executor-ref"; static final String REMOTE_SERVICE_REF_ATTRIBUTE = "remote-service-ref"; static final String LIVE_OBJECT_SERVICE_REF_ATTRIBUTE = "live-object-service-ref"; static final String OBJECT_ID_REF_ATTRIBUTE = "object-id-ref"; static final String MAX_IDLE_ATTRIBUTE = "max-idle"; static final String TIME_TO_LIVE_ATTRIBUTE = "time-to-live"; static final String MAX_IDLE_UNIT_ATTRIBUTE = "max-idle-unit"; static final String TIME_TO_LIVE_UNIT_ATTRIBUTE = "time-to-live-unit"; static final String CONCURRENT_WORKERS_ATTRIBUTE = "concurrent-workers"; static final String WITHIN_ATTRIBUTE = "within"; static final String TIME_UNIT_ATTRIBUTE = "time-unit"; static final String API_CLASS_ATTRIBUTE = "api-class"; static final String CLASS_ATTRIBUTE = "class"; static final String OBJECT_ID_ATTRIBUTE = "object-id"; static final String READ_LOCK_ELEMENT = "read-lock"; static final String WRITE_LOCK_ELEMENT = "write-lock"; static final String RPC_SERVER_ELEMENT = "rpc-server"; static final String RPC_CLIENT_ELEMENT = "rpc-client"; static final String REMOTE_INVOCATION_OPTIONS_ELEMENT = "remote-invocation-options"; static final String REMOTE_NO_ACK_ELEMENT = "remote-no-ack"; static final String REMOTE_ACK_ELEMENT = "remote-ack"; static final String REMOTE_NO_RESULT_ELEMENT = "remote-no-result"; static final String REMOTE_RESULT_ELEMENT = "remote-result"; static final String LOCAL_CACHED_MAP_OPTIONS_ELEMENT = "local-cached-map-options"; static final String LIVE_OBJECT_ELEMENT = "live-object"; static final String LIVE_OBJECT_REGISTRATION_ELEMENT = "live-object-registration"; public String[] parseAliase(Element element) { if (element == null) { return null; } String[] aliases = null; String name = element.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(name)) { aliases = StringUtils.trimArrayElements( StringUtils.commaDelimitedListToStringArray(name)); } return aliases; } public BeanDefinitionBuilder createBeanDefinitionBuilder(Element element, ParserContext parserContext, Class<?> cls) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); builder.getRawBeanDefinition().setBeanClass(cls); builder.getRawBeanDefinition() .setSource(parserContext.extractSource(element)); if (parserContext.isNested()) { builder.setScope(parserContext.getContainingBeanDefinition() .getScope()); } if (parserContext.isDefaultLazyInit()) { builder.setLazyInit(true); } return builder; } public BeanComponentDefinition registerBeanDefinition(BeanDefinitionBuilder builder, String id, String[] aliases, ParserContext parserContext) { BeanDefinitionHolder holder = new BeanDefinitionHolder(builder.getBeanDefinition(), id, aliases); BeanDefinitionReaderUtils .registerBeanDefinition(holder, parserContext.getRegistry()); BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); parserContext.registerComponent(componentDefinition); return componentDefinition; } public BeanComponentDefinition registerBeanDefinition(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) { BeanDefinitionHolder holder = new BeanDefinitionHolder(builder.getBeanDefinition(), getId(element, builder, parserContext), parseAliase(element)); BeanDefinitionReaderUtils .registerBeanDefinition(holder, parserContext.getRegistry()); BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); parserContext.registerComponent(componentDefinition); return componentDefinition; } public void addConstructorArgs(Element element, String attribute, Class type, BeanDefinition bd) { if (element.hasAttribute(attribute)) { addConstructorArgs(element.getAttribute(attribute), type, bd); } } public void addConstructorArgs(Object value, Class type, BeanDefinition bd) { ConstructorArgumentValues.ValueHolder vHolder = new ConstructorArgumentValues.ValueHolder(value, type.getName()); ConstructorArgumentValues args = bd.getConstructorArgumentValues(); args.addIndexedArgumentValue(args.getArgumentCount(), vHolder); } public void addConstructorArgs(Element element, String attribute, Class type, BeanDefinitionBuilder builder) { addConstructorArgs(element, attribute, type, builder.getRawBeanDefinition()); } public void addConstructorArgs(Object value, Class type, BeanDefinitionBuilder builder) { addConstructorArgs(value, type, builder.getRawBeanDefinition()); } public String getName(Node node) { return Conventions.attributeNameToPropertyName(node.getLocalName()); } public String getId(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) { String id = element != null ? element.getAttribute(ID_ATTRIBUTE) : null; if (!StringUtils.hasText(id)) { id = generateId(builder, parserContext); } return id; } public String generateId(BeanDefinitionBuilder builder, ParserContext parserContext) { return parserContext.getReaderContext() .generateBeanName(builder.getRawBeanDefinition()); } public void populateIdAttribute(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) { if (element == null) { return; } if (!StringUtils.hasText(element.getAttribute(ID_ATTRIBUTE))) { element.setAttribute(ID_ATTRIBUTE, generateId(builder, parserContext)); } } public BeanComponentDefinition factoryInvoker(Element element, String bean, String method, Object[] args, ParserContext parserContext) { BeanDefinitionBuilder builder = preInvoke(element, bean, method, args, parserContext, true); builder.addPropertyReference("targetObject", bean); return doInvoke(element, builder, parserContext); } public BeanComponentDefinition factoryInvoker(Element element, Object obj, String method, Object[] args, ParserContext parserContext) { BeanDefinitionBuilder builder = preInvoke(element, obj, method, args, parserContext, true); builder.addPropertyValue("targetObject", obj); return doInvoke(element, builder, parserContext); } public BeanComponentDefinition factoryInvoker(String bean, String method, Object[] args, ParserContext parserContext) { return factoryInvoker(null, bean, method, args, parserContext); } public BeanComponentDefinition factoryInvoker(Object obj, String method, Object[] args, ParserContext parserContext) { return factoryInvoker(null, obj, method, args, parserContext); } public BeanComponentDefinition invoker(Element element, String bean, String method, Object[] args, ParserContext parserContext) { BeanDefinitionBuilder builder = preInvoke(element, bean, method, args, parserContext, false); builder.addPropertyReference("targetObject", bean); return doInvoke(element, builder, parserContext); } public BeanComponentDefinition invoker(Element element, Object obj, String method, Object[] args, ParserContext parserContext) { BeanDefinitionBuilder builder = preInvoke(element, obj, method, args, parserContext, false); builder.addPropertyValue("targetObject", obj); return doInvoke(element, builder, parserContext); } public BeanComponentDefinition invoker(String bean, String method, Object[] args, ParserContext parserContext) { return invoker(null, bean, method, args, parserContext); } public BeanComponentDefinition invoker(Object obj, String method, Object[] args, ParserContext parserContext) { return invoker(null, obj, method, args, parserContext); } private BeanDefinitionBuilder preInvoke(Element element, Object obj, String method, Object[] args, ParserContext parserContext, boolean factory) { BeanDefinitionBuilder builder = createBeanDefinitionBuilder(element, parserContext, factory ? MethodInvokingFactoryBean.class : BeanMethodInvoker.class); if (obj instanceof Class) { builder.addPropertyValue("staticMethod", ((Class) obj).getName() + "." + method); } else { builder.addPropertyValue("targetMethod", method); } builder.addPropertyValue("arguments", args); if (element != null) { parserContext.getDelegate().parseQualifierElements(element, builder.getRawBeanDefinition()); } return builder; } private BeanComponentDefinition doInvoke(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) { String id = getId(element, builder, parserContext); return registerBeanDefinition(builder, id, parseAliase(element), parserContext); } public boolean isEligibleAttribute(String attributeName) { return !attributeName.equals("xmlns") && !attributeName.startsWith("xmlns:") && !ID_ATTRIBUTE.equals(attributeName) && !NAME_ATTRIBUTE.equals(attributeName); } public boolean isEligibleAttribute(Attr attribute) { return isEligibleAttribute(attribute.getName()); } public boolean isRedissonNS(Node node) { return node != null && REDISSON_NAMESPACE.equals(node.getNamespaceURI()); } public String getAttribute(Element element, String attribute) { return element.getAttribute(attribute); } public void setAttribute(Element element, String attribute, String value) { element.setAttribute(attribute, value); } public boolean hasAttribute(Element element, String attribute) { return element.hasAttribute(attribute); } public boolean hasElement(Element element, String tagName) { return element.getElementsByTagNameNS( RedissonNamespaceParserSupport.REDISSON_NAMESPACE, tagName) .getLength() > 0; } public Element getSingleElement(Element element, String tagName) { return (Element) element.getElementsByTagNameNS( RedissonNamespaceParserSupport.REDISSON_NAMESPACE, tagName) .item(0); } }